package com.dmc.scrabblator;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import com.dmc.scrabblator.structures.CharVal;

public class WordTrie
{

	Map<Character, Map> trieMap;
	private final static char WORD_CHAR = '0';//if this is present, this is a word terminator!

	public final static char BLANK_CHAR = ' ';

	private final static int HASH_SIZE = 5;

	public WordTrie()
	{
		trieMap = new HashMap<Character, Map>(27);
	}

	public void generate(List<String> words)
	{
		for (String w : words)
		{
			char[] chars = w.toCharArray();
			process(chars, 0, trieMap);
		}
	}

	/**
	 * 
	 * @param f
	 * @throws Exception Will throw any exception
	 */
	public void generate(File f) throws Exception
	{

		BufferedReader br = new BufferedReader(new FileReader(f));

		String w;
		while ((w = br.readLine()) != null)
		{
			char[] chars = w.toCharArray();
			//System.out.println(w);
			process(chars, 0, trieMap);
		}

	}

	private Map<Character, Map> getMapFromChar(char c, Map<Character, Map> trie)
	{
		Map<Character, Map> m;

		m = trie.get(c);
		if (m == null)
		{
			m = new HashMap<Character, Map>(HASH_SIZE);
			trie.put(c, m);
		}

		return m;
	}

	private void process(char[] chars, int pos, Map<Character, Map> map)
	{
		Map<Character, Map> m = getMapFromChar(chars[pos], map);
		if (pos < chars.length - 1)
		{
			process(chars, pos + 1, m);
		}
		else
		{
			//This is unfortunate but necessary for now.  make it better?
			m.put(WORD_CHAR, new HashMap<Character, Map>(HASH_SIZE));
		}
	}

	public Set<WordValue> getFromList(String find)
	{
		Set<WordValue> matches = new TreeSet<WordValue>();
		//do recursive work here

		List<Character> list = makeList(find);
		getMatches(list, new ArrayList<CharVal>(), 0, trieMap, matches);
		return matches;
	}

	/**
	 * 
	 * @param remaining Remaining chars
	 * @param prefix The current potential word from the start
	 * @param trie The current subtrie
	 * @param matches The list of word matches+values
	 */
	private void getMatches(List<Character> remaining, List<CharVal> prefix,
			int runTotal, Map<Character, Map> trie, Set<WordValue> matches)
	{
		//Found a word!!
		if (trie.get(WORD_CHAR) != null)
		{
			matches.add(new WordValue(prefix, runTotal));
		}
		//Look for potentials
		int remainingSize = remaining.size();
		for (int i = 0; i < remainingSize; i++)
		{
			Map<Character, Map> nextMap = trie.get(remaining.get(i));
			if (nextMap != null || remaining.get(i) == BLANK_CHAR)//Optimize by not going in if we only have a WORD_CHAR
			{
				//Dupe list, remove i, add char to prefix, recurse
				char curChar = remaining.remove(i);//Don't dupe - just remove for now
				boolean blank = (curChar == BLANK_CHAR);
				//TODO: Deal with blank here
				Set<Character> set;
				if (blank)
				{//Can use any ele from the list!
					set = trie.keySet();
					for (char c : set)
					{
						prefix.add(new CharVal(c, blank));
						//runTotal += WordValueUtil.getCharVal(c);
						nextMap = trie.get(c); //have to re-do this for the blank case.
						getMatches(remaining, prefix, runTotal, nextMap,
								matches);

						//runTotal -= WordValueUtil.getCharVal(c);
						prefix.remove(prefix.size() - 1);//Remove the cur val
						//remaining.add(i, c);
					}
					remaining.add(i, curChar);
				}
				else
				{
					prefix.add(new CharVal(curChar, blank));
					runTotal+=WordValueUtil.getCharVal(curChar);
					getMatches(remaining, prefix, runTotal, nextMap, matches);
					runTotal -= WordValueUtil.getCharVal(curChar);
					prefix.remove(prefix.size() - 1);//Remove the cur val
					remaining.add(i, curChar);
				}

			}
		}
	}

	////////////UTIL
	private List<Character> makeList(String s)
	{
		List<Character> list = new ArrayList<Character>();
		for (char c : s.toCharArray())
		{
			list.add(c);
		}
		return list;
	}

	private String makeWord(List<Character> word)
	{
		StringBuilder s = new StringBuilder();
		for (char c : word)
		{
			s.append(c);
		}
		return s.toString();
	}
}
